import Vue from 'vue'
import AudioPlayer from './audio-player'

class Stage {
  constructor(props = {}) {
    const {
      beforeRunAction = () => {}, // 执行前
      name = '', // 名称
      questionIndex = 0, // 所属问题序号
      questionStem = '', // 所属问题题干
      isQuestionReadonly = false, // 所属问题是否只读
      operateTips = '', // 提示文案
      duration = 0, // 持续时间 (秒数/音频文件/异步任务)
      ignoreSpeed = false, // 忽略倍速
      ignoreCountdown = false, // 忽略倒计时
      afterRunAction = () => {} // 执行后
    } = props

    this.beforeRunAction = beforeRunAction
    this.name = name
    this.questionIndex = questionIndex
    this.questionStem = questionStem
    this.isQuestionReadonly = isQuestionReadonly
    this.operateTips = operateTips
    this.duration = duration
    this.ignoreSpeed = ignoreSpeed
    this.ignoreCountdown = ignoreCountdown
    this.afterRunAction = afterRunAction
  }
}

const ControllerStatus = {
  NORMAL: 0, // 正常
  PAUSE: 1 // 暂停
}

class Controller {
  constructor() {
    // id
    this._uid = new Date().getTime()
    // 状态 normal => 正常 pause => 暂停
    this.status = ControllerStatus.NORMAL
    // 倍速
    this.speed = 1
    // 音频播放器
    this.audioPlayer = new AudioPlayer()
    this.audioPlayer.init()
    // 操作区文案
    this.operateTips = ''
    // 当前阶段
    this.stageIndex = 0
    // 所有阶段的集合
    this.stages = []
    this.actionTimer = null
    this.countdownTimer = null
  }

  /**
   * 初始化
   * @param {*} stages 所有阶段的集合
   * @param {*} stageIndex 初始阶段序号
   * @returns
   */
  init(stages = [], stageIndex = 0) {
    if (!stages || stages.length === 0) return

    this.stages = stages.map((stage) => new Stage(stage))
    this.runAction(stageIndex)
  }

  destroy() {
    this.audioPlayer.stop()
    clearTimeout(this.actionTimer)
    clearTimeout(this.countdownTimer)
  }

  /**
   * 重置
   */
  reset() {
    this.status = ControllerStatus.NORMAL
    this.operateTips = ''
    this.stageIndex = 0
    this.stages = []
    this.actionTimer = null
    this.countdownTimer = null
  }

  /**
   * 设置控制器状态
   * @param {*} status 状态
   * @returns
   */
  setStatus(status = 'normal') {
    switch (status) {
      case 'pause':
        this.audioPlayer.pause()
        this.status = ControllerStatus.PAUSE
        return
      default:
        const { duration } = this.stages[this.stageIndex]
        this.isAudioSrc(duration) && this.audioPlayer.play()
        this.status = ControllerStatus.NORMAL
        return
    }
  }

  /**
   * 获取控制器状态
   * @returns
   */
  getStatus() {
    switch (this.status) {
      case ControllerStatus.PAUSE:
        return 'pause'
      default:
        return 'normal'
    }
  }

  /**
   * 判断是否音频文件地址
   * @param {*} str
   */
  isAudioSrc(str) {
    if (typeof str != 'string') return

    return str.search(/[a-zA-z]+:\/\/[^\s]*/, '') != -1 ? true : false
  }

  /**
   * 设置倍速
   * @param {*} type 类型，放慢/加快
   * @returns
   */
  changeSpeed(type = 'normal') {
    let baseSpeed = 1.0
    let gap = 0.2
    let nextSpeed
    switch (type) {
      // 加快
      case 'turnUp':
        nextSpeed = this.speed + gap
        this.speed = parseFloat((nextSpeed <= baseSpeed ? baseSpeed + gap : nextSpeed).toFixed(1))
        break
      // 放慢
      case 'turnDown':
        nextSpeed = this.speed - gap
        this.speed = parseFloat(
          (nextSpeed >= 1 ? baseSpeed - gap : nextSpeed <= 0 ? gap : nextSpeed).toFixed(1)
        )
        break
      default:
        this.speed = baseSpeed
        break
    }

    if (!this.audioPlayer.context) return
    this.audioPlayer.context.playbackRate = this.speed
  }

  /**
   * 获取当前阶段数据
   * @returns
   */
  getCurrentStage() {
    return this.stages[this.stageIndex]
  }

  /**
   * 执行阶段任务
   * @param {*} stageIndex 阶段序号
   * @returns
   */
  async runAction(stageIndex = 0) {
    clearTimeout(this.actionTimer)

    if (stageIndex < 0) {
      this.runAction(0)
      return
    }

    if (stageIndex > this.stages.length - 1) {
      this.onSubmit()
      return
    }

    /**
     * 剩余时间倒计时
     * currentTime => 当前时间(秒)
     * totalTime => 总时间(秒)
     * immediate => 是否立即执行
     * unloop => 不递归执行
     */
    const countdown = (currentTime = 0, totalTime, immediate = false, unloop = false) => {
      const { formatSeconds } = Vue.prototype.$utils

      if (currentTime >= totalTime) {
        clearTimeout(this.countdownTimer)
        this.countdownTimer = setTimeout(
          () => {
            if (this.status === ControllerStatus.PAUSE) {
              this.countdownTimer = setTimeout(() => {
                countdown(currentTime, totalTime, immediate, unloop)
              }, 1000)
              return
            }

            const { afterRunAction } = this.stages[stageIndex]
            !ignoreCountdown && (this.operateTips = `${operateTips} ${formatSeconds(0)}`)
            afterRunAction()
            this.runAction(stageIndex + 1)
          },
          immediate ? 0 : 1000 / (ignoreSpeed ? 1 : this.speed)
        )
        return
      }

      clearTimeout(this.countdownTimer)
      this.countdownTimer = setTimeout(
        () => {
          if (this.status === ControllerStatus.PAUSE) {
            this.countdownTimer = setTimeout(() => {
              countdown(currentTime, totalTime, immediate, unloop)
            }, 1000)
            return
          }

          const seconds = totalTime - currentTime
          !ignoreCountdown && (this.operateTips = `${operateTips} ${formatSeconds(seconds)}`)
          !unloop && countdown(currentTime + 1, totalTime)
        },
        immediate ? 0 : 1000 / (ignoreSpeed ? 1 : this.speed)
      )
    }

    // 初始化
    this.onStageIndexChange(stageIndex)
    this.status = ControllerStatus.NORMAL
    this.audioPlayer.off('onTimeUpdate', this._uid)
    this.audioPlayer.off('onError', this._uid)
    this.audioPlayer.stop()
    clearTimeout(this.countdownTimer)
    const { beforeRunAction, operateTips, duration, ignoreSpeed, ignoreCountdown } =
      this.stages[stageIndex]
    beforeRunAction()
    this.operateTips = operateTips
    this.stageIndex = stageIndex

    switch (typeof duration) {
      case 'string':
        if (this.isAudioSrc(duration)) {
          const audioCtx = this.audioPlayer.context
          const onTimeUpdate = () => {
            let currentTime, totalTime
            if (audioCtx.currentTime > 1) {
              currentTime = Math.floor(audioCtx.currentTime)
              totalTime = Math.floor(audioCtx.duration)
            } else {
              currentTime =
                audioCtx.currentTime < audioCtx.duration
                  ? Math.floor(audioCtx.currentTime)
                  : Math.ceil(audioCtx.currentTime)
              totalTime = Math.ceil(audioCtx.duration)
            }

            if (currentTime >= 0 && totalTime) {
              countdown(currentTime, totalTime, true, true)
            }
          }
          const onError = () => {
            Vue.prototype.$message.error('播放音频失败')
            this.runAction(stageIndex + 1)
          }
          this.audioPlayer.on('onTimeUpdate', this._uid, onTimeUpdate)
          this.audioPlayer.on('onError', this._uid, onError)
          audioCtx.src = duration
          audioCtx.playbackRate = this.speed
          audioCtx.play()
        } else {
          this.runAction(stageIndex + 1)
        }
        break
      case 'number':
        countdown(0, duration / 1000, true)
        break
      case 'function':
        await duration()
        this.runAction(stageIndex + 1)
        break
      default:
        this.runAction(stageIndex + 1)
        break
    }
  }

  /**
   * 跳到上一阶段
   */
  toPrevStage() {
    this.runAction(this.stageIndex - 1)
  }

  /**
   * 跳到下一阶段
   */
  toNextStage() {
    this.runAction(this.stageIndex + 1)
  }

  /**
   * 阶段索引改变
   */
  onStageIndexChange() {}

  /**
   * 所有阶段执行完毕，进入下一题/提交答案
   */
  onSubmit() {}
}

export default Controller
